#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>

#include "imbus_internal.h"
#include "im_io_channel.h"
#include "im_io_unix.h"

static IMBool
__im_io_channel_def_valid       (IMIOChannel    *channel)
{
    return FALSE;
}
static IMBool
__im_io_channel_def_read        (IMIOChannel    *channel,
                                 IMChar         *buf,
                                 IMSize          count,
                                 IMSize         *bytes_read,
                                 IMIOError      *error)
{
    if (bytes_read) *bytes_read = 0;
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMBool
__im_io_channel_def_write       (IMIOChannel    *channel,
                                 const IMChar   *buf,
                                 IMSize          count,
                                 IMSize         *bytes_written,
                                 IMIOError      *error)
{
    if (bytes_written) *bytes_written = 0;
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMBool
__im_io_channel_def_seek        (IMIOChannel    *channel,
                                 IMInt64         offset,
                                 IMIOSeekType    type,
                                 IMIOError      *error)
{
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMBool
__im_io_channel_def_tell        (IMIOChannel    *channel,
                                 IMUInt64       *offset,
                                 IMIOError      *error)
{
    if (offset) *offset = 0;
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMBool
__im_io_channel_def_close       (IMIOChannel    *channel,
                                 IMIOError      *error)
{
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMPointer
__im_io_channel_def_mmap        (IMIOChannel    *channel,
                                 IMUInt64        offset,
                                 IMSize          length,
                                 IMBool          writeable,
                                 IMBool          shared,
                                 IMIOError      *error)
{
    if (error) *error = IM_IO_ERROR_FAILED;
    return 0;
}
static IMBool
__im_io_channel_def_munmap      (IMIOChannel    *channel,
                                 IMPointer       address,
                                 IMSize          length,
                                 IMIOError      *error)
{
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMBool
__im_io_channel_def_set_flags   (IMIOChannel    *channel,
                                 IMUInt          flags,
                                 IMIOError      *error)
{
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMBool
__im_io_channel_def_get_flags   (IMIOChannel    *channel,
                                 IMUInt         *flags,
                                 IMIOError      *error)
{
    return 0;
}
static IMBool
__im_io_channel_def_wait_for_readable (IMIOChannel    *channel,
                                       IMInt          *timeout,
                                       IMIOError      *error)
{
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMBool
__im_io_channel_def_read_with_timeout (IMIOChannel    *channel,
                                       IMChar         *buf,
                                       IMSize          count,
                                       IMSize         *bytes_read,
                                       IMInt          *timeout,
                                       IMIOError      *error)
{
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMBool
__im_io_channel_def_wait_for_writeable(IMIOChannel    *channel,
                                       IMInt          *timeout,
                                       IMIOError      *error)
{
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static IMBool
__im_io_channel_def_write_with_timeout(IMIOChannel    *channel,
                                       const IMChar   *buf,
                                       IMSize          count,
                                       IMSize         *bytes_written,
                                       IMInt          *timeout,
                                       IMIOError      *error)
{
    if (error) *error = IM_IO_ERROR_FAILED;
    return FALSE;
}
static void
__im_io_channel_copy (IMPointer      dest,
                      IMConstPointer src)
{
    _im_assert_not_reached("IMIOChannel objects couldn't be copied.\n");
}

static IMBool
__im_io_channel_serialize (IMConstPointer obj,
                           IMPointer      stream)
{
    _im_assert_not_reached("IMIOChannel objects couldn't be serialized.\n");
    return FALSE;
}

static IMBool
__im_io_channel_deserialize (IMPointer      obj,
                             IMPointer      stream)
{
    _im_assert_not_reached("IMIOChannel objects couldn't be deserialized.\n");
    return FALSE;
}

static void
__im_io_channel_class_init (IMPointer klass)
{
    IMObjectClass    *ocp = (IMObjectClass *)klass;
    IMIOChannelClass *cp = (IMIOChannelClass *)klass;

    ocp->copy       = __im_io_channel_copy;
    ocp->serialize  = __im_io_channel_serialize;
    ocp->deserialize= __im_io_channel_deserialize;

    cp->valid       = __im_io_channel_def_valid;
    cp->read        = __im_io_channel_def_read;
    cp->write       = __im_io_channel_def_write;
    cp->seek        = __im_io_channel_def_seek;
    cp->tell        = __im_io_channel_def_tell;
    cp->close       = __im_io_channel_def_close;
    cp->mmap        = __im_io_channel_def_mmap;
    cp->munmap      = __im_io_channel_def_munmap;
    cp->set_flags   = __im_io_channel_def_set_flags;
    cp->get_flags   = __im_io_channel_def_get_flags;
    cp->wait_for_readable = __im_io_channel_def_wait_for_readable;
    cp->read_with_timeout = __im_io_channel_def_read_with_timeout;
    cp->wait_for_writeable= __im_io_channel_def_wait_for_writeable;
    cp->write_with_timeout= __im_io_channel_def_write_with_timeout;
}

static void
__im_io_channel_initialize (IMPointer obj)
{
    IMIOChannel *p = IM_IO_CHANNEL(obj);
    _im_assert (p);
    p->close_on_unref = FALSE;
}

static void
__im_io_channel_finalize (IMPointer obj)
{
}

void
__im_type_register_io_channel ()
{
    static IMTypeInfo im_io_channel_type_info =
    {
        sizeof (IMIOChannelClass),  /**< class_size >**/
        __im_io_channel_class_init, /**< class_init >**/
        0,                          /**< class_finalize >**/

        sizeof (IMIOChannel),       /**< instance_size >**/
        __im_io_channel_initialize, /**< instance_init >**/
        __im_io_channel_finalize,   /**< instance_finalize >**/
    };

    __im_type_register_builtin_class (IM_TYPE_INITIALLY_OWNED,
                                      IM_TYPE_IO_CHANNEL,
                                      "IOChannel",
                                      &im_io_channel_type_info,
                                      FALSE);
}

IMBool
im_io_channel_valid (IMIOChannel *channel)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->valid(channel);
}

IMBool
im_io_channel_read (IMIOChannel *channel, IMChar *buf, IMSize count,
                    IMSize *bytes_read, IMIOError *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    _im_return_val_if_fail (buf, FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->read (
            channel, buf, count, bytes_read, error);
}

IMBool
im_io_channel_write (IMIOChannel *channel, const IMChar *buf, IMSize count,
                     IMSize *bytes_written, IMIOError *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    _im_return_val_if_fail (buf, FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->write (
            channel, buf, count, bytes_written, error);
}

IMBool
im_io_channel_seek (IMIOChannel *channel, IMInt64 offset,
                    IMIOSeekType type, IMIOError *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->seek (channel, offset, type, error);
}

IMBool
im_io_channel_tell (IMIOChannel *channel, IMUInt64 *offset, IMIOError *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->tell (channel, offset, error);
}

void
im_io_channel_close (IMIOChannel *channel)
{
    IMIOError error;
    _im_return_if_fail (IM_IS_IO_CHANNEL(channel));
    IM_IO_CHANNEL_GET_CLASS(channel)->close (channel, &error);
}

IMPointer
im_io_channel_mmap (IMIOChannel *channel, IMUInt64 offset, IMSize length,
                    IMBool writeable, IMBool shared, IMIOError *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), 0);
    return IM_IO_CHANNEL_GET_CLASS(channel)->mmap (
            channel, offset, length, writeable, shared, error);
}

IMBool
im_io_channel_munmap (IMIOChannel *channel, IMPointer address,
                      IMSize length, IMIOError *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->munmap (channel, address, length, error);
}

IMBool
im_io_channel_get_flags (IMIOChannel *channel, IMUInt *flags, IMIOError *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    _im_return_val_if_fail (flags != 0, FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->get_flags (channel, flags, error);
}

IMBool
im_io_channel_set_flags (IMIOChannel *channel, IMUInt flags,
                                    IMIOError *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->set_flags (channel, flags, error);
}

IMBool
im_io_channel_set_append (IMIOChannel *channel, IMBool append, IMIOError *error)
{
    IMUInt flags;
    if (im_io_channel_get_flags (channel, &flags, error)) {
        if (append) flags |= IM_IO_FLAG_APPEND;
        else flags &= ~IM_IO_FLAG_APPEND;
        return im_io_channel_set_flags (channel, flags, error);
    }
    return FALSE;
}

IMBool
im_io_channel_set_nonblock (IMIOChannel *channel, IMBool nonblock, IMIOError *error)
{
    IMUInt flags;
    if (im_io_channel_get_flags (channel, &flags, error)) {
        if (nonblock) flags |= IM_IO_FLAG_NONBLOCK;
        else flags &= ~IM_IO_FLAG_NONBLOCK;
        return im_io_channel_set_flags (channel, flags, error);
    }
    return FALSE;
}

IMBool
im_io_channel_is_append (IMIOChannel *channel)
{
    IMUInt flags;
    return (im_io_channel_get_flags (channel, &flags, 0) &&
            (flags & IM_IO_FLAG_APPEND) != 0);
}

IMBool
im_io_channel_is_nonblock (IMIOChannel *channel)
{
    IMUInt flags;
    return (im_io_channel_get_flags (channel, &flags, 0) &&
            (flags & IM_IO_FLAG_NONBLOCK) != 0);
}

IMBool
im_io_channel_is_readable (IMIOChannel *channel)
{
    IMUInt flags;
    return (im_io_channel_get_flags (channel, &flags, 0) &&
            (flags & IM_IO_FLAG_IS_READABLE) != 0);
}

IMBool
im_io_channel_is_writeable (IMIOChannel *channel)
{
    IMUInt flags;
    return (im_io_channel_get_flags (channel, &flags, 0) &&
            (flags & IM_IO_FLAG_IS_WRITEABLE) != 0);
}

IMBool
im_io_channel_is_seekable (IMIOChannel *channel)
{
    IMUInt flags;
    return (im_io_channel_get_flags (channel, &flags, 0) &&
            (flags & IM_IO_FLAG_IS_SEEKABLE) != 0);
}

IMBool
im_io_channel_is_mmapable (IMIOChannel *channel)
{
    IMUInt flags;
    return (im_io_channel_get_flags (channel, &flags, 0) &&
            (flags & IM_IO_FLAG_IS_MMAPABLE) != 0);
}

void
im_io_channel_set_close_on_unref (IMIOChannel *channel, IMBool do_close)
{
    _im_return_if_fail (IM_IS_IO_CHANNEL(channel));
    channel->close_on_unref = do_close;
}

IMBool
im_io_channel_get_close_on_unref (IMIOChannel *channel)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    return channel->close_on_unref;
}

IMBool
im_io_channel_wait_for_readable (IMIOChannel    *channel,
                                 IMInt          *timeout,
                                 IMIOError      *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->wait_for_readable (channel,
                                                                timeout,
                                                                error);
}

IMBool
im_io_channel_read_with_timeout (IMIOChannel    *channel,
                                 IMChar         *buf,
                                 IMSize          count,
                                 IMSize         *bytes_read,
                                 IMInt          *timeout,
                                 IMIOError      *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    _im_return_val_if_fail (buf, FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->read_with_timeout (channel,
                                                                buf,
                                                                count,
                                                                bytes_read,
                                                                timeout,
                                                                error);
}

IMBool
im_io_channel_wait_for_writeable (IMIOChannel    *channel,
                                  IMInt          *timeout,
                                  IMIOError      *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->wait_for_writeable (channel,
                                                                 timeout,
                                                                 error);
}

IMBool
im_io_channel_write_with_timeout (IMIOChannel    *channel,
                                  const IMChar   *buf,
                                  IMSize          count,
                                  IMSize         *bytes_written,
                                  IMInt          *timeout,
                                  IMIOError      *error)
{
    _im_return_val_if_fail (IM_IS_IO_CHANNEL(channel), FALSE);
    _im_return_val_if_fail (buf, FALSE);
    return IM_IO_CHANNEL_GET_CLASS(channel)->write_with_timeout (channel,
                                                                 buf,
                                                                 count,
                                                                 bytes_written,
                                                                 timeout,
                                                                 error);
}

IMIOError
im_io_channel_error_from_errno (int err)
{
    switch (err) {
#ifdef EACCES
        case EACCES:    return IM_IO_ERROR_ACCES;
#endif
#ifdef EAGAIN
        case EAGAIN:    return IM_IO_ERROR_AGAIN;
#endif
#ifdef EBADF
        case EBADF:     return IM_IO_ERROR_BADF;
#endif
#ifdef EFAULT
        case EFAULT:    return IM_IO_ERROR_FAULT;
#endif
#ifdef EFBIG
        case EFBIG:     return IM_IO_ERROR_FBIG;
#endif
#ifdef EINTR
        /* In general, we should catch EINTR before we get here,
         * but close() is allowed to return EINTR by POSIX, so
         * we need to catch it here; EINTR from close() is
         * unrecoverable, because it's undefined whether
         * the fd was actually closed or not, so we just return
         * a generic error code.
         */
        case EINTR:     return IM_IO_ERROR_INTR;
#endif
#ifdef EINVAL
        case EINVAL:    return IM_IO_ERROR_INVAL;
#endif
#ifdef EIO
        case EIO:       return IM_IO_ERROR_IO;
#endif
#ifdef EISDIR
        case EISDIR:    return IM_IO_ERROR_ISDIR;
#endif
#ifdef ENFILE
        case ENFILE:    return IM_IO_ERROR_NFILE;
#endif
#ifdef ENODEV
        case ENODEV:    return IM_IO_ERROR_NODEV;
#endif
#ifdef ENOMEM
        case ENOMEM:    return IM_IO_ERROR_NOMEM;
#endif
#ifdef ENOSPC
        case ENOSPC:    return IM_IO_ERROR_NOSPC;
#endif
#ifdef ENXIO
        case ENXIO:     return IM_IO_ERROR_NXIO;
#endif
#ifdef EOVERFLOW
        case EOVERFLOW: return IM_IO_ERROR_OVERFLOW;
#endif
#ifdef EPIPE
        case EPIPE:     return IM_IO_ERROR_PIPE;
#endif
#ifdef ESPIPE
        case ESPIPE:     return IM_IO_ERROR_SPIPE;
#endif
        default:
            return IM_IO_ERROR_FAILED;
    }
}

IMIOChannel*
im_io_channel_new_file (const IMChar   *filename,
                        const IMChar   *mode,
                        IMIOError      *error)
{
    return im_io_channel_unix_new_file (filename, mode, error);
}


/*
vi:ts=4:nowrap:ai:expandtab
*/
